package tcp;

import java.io.IOException;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 
 * @author Kristopher L. Kalish, James Madison University
 * @version 1 - April 16, 2008
 *
 * @param <T>
 */
public class HashableTCPServer<T> extends TCPServer
{
	private Map<T, SessionHandler>  clientToSession; 
	private Map<T, List<Object>>    clientToData;
	
	public HashableTCPServer(int listenPort)
	{
		super(listenPort);
		clientToSession = new Hashtable<T, SessionHandler>();
		clientToData    = new Hashtable<T, List<Object>>();
	}
	
	/**
	 * Since you can register yourself as a new connection
	 * listener and be notified of the socket created, other
	 * classes will know about the socket... we'll then 
	 * @param key
	 * @param socket
	 */
	public void addHash(T key, SessionHandler sh)
	{
		clientToSession.put(key, sh);
	}
	
	public boolean sessionKeyExists(T key)
	{
		return clientToSession.containsKey(key);
	}
	protected void performSendTos() throws IOException
	{
		System.out.println("Sending to all...");
		Set<T> keys = clientToData.keySet();
		
		for(T key : keys)
		{
			List<Object> objs = clientToData.get(key);
			SessionHandler sh = clientToSession.get(key);
			
			if(sh != null)
			{
				for(Object obj : objs)
				{
					System.out.println("Hashable go: " + obj); //debug
					sh.send(obj);
				}
			}
		}
		
		clientToData.clear();
		
	}
	
	public void run()
	{
		while(running)
		{
			synchronized(lock)
			{
				try 
				{
					//System.out.println("DAR"); //debug
					performSendAll();
					performSendTos();
				}
				catch(IOException ioe)
				{
					ioe.printStackTrace();
				}
				
				try
				{
					lock.wait();
				}
				catch(InterruptedException ie)
				{
					ie.printStackTrace();
				}
			}
		}
		
		// we finished running, clean up
		try
		{
			for(SessionHandler session : sessions)
				session.cleanup();
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
	
	public void sendTo(T clientKey, Object data)
	{
		synchronized(lock)
		{
			// check if there's already data to send
			if(clientToData.get(clientKey) != null) 
			{
				clientToData.get(clientKey).add(data);
			}
			else // there's no data already in the queue, make one
			{
				List<Object> objQueue = new LinkedList<Object>();
				objQueue.add(data);
				clientToData.put(clientKey, objQueue);
			}
			lock.notifyAll();
		}
	}
}
